﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;

namespace URM_SVN
{
    public enum RegisterOperateType
    {
        ToZero,
        Read,
        Write,
        Increase
    }

    public delegate void ShowRegisterChange(int _index, ref int _value, RegisterOperateType _op);
    public delegate void ShowInstruction(int _index, Instruction _instruction, Urm _registerStatus);
    public delegate void ShowURMstatus( Urm _URM );
    public delegate void SendToForm( string _text ); 

    public enum CommandType
    {
        Z,
        S,
        T,
        J
    }

    public struct Instruction
    {
        public CommandType Command;
        public int FirstIndex;
        public int SecondIndex;
        public int ThirdIndex;
    }


    public class Daemon
    {
        int index = 0; // index of the instruction (in the instructionTable) to execute
        
        public event ShowRegisterChange EventRegisterOperate;
        public event ShowURMstatus EventSetRegisterStatus;
        public event ShowInstruction EventPrepareDoInstruction;
        public event EventHandler EventReloadInstruction;
        public event EventHandler EventSetStartFinish;
        public event SendToForm EventAddInstruction;
        public event SendToForm EventPrintf;
        public event SendToForm Error;
        private bool initData = false;


        List<Instruction> instructionTable = new List<Instruction>();
        List<int> Start = new List<int>();
        Urm RegisterState = new Urm();
        string patternZ = @"^I\d+\s*Z\s*\(\s*(?<FirstIndex>\d+)\s*\)\s*$";
        string patternS = @"^I\d+\s*S\s*\(\s*(?<FirstIndex>\d+)\s*\)\s*$";
        string patternT = @"^I\d+\s*T\s*\(\s*(?<FirstIndex>\d+)\s*,\s*(?<SecondIndex>\d+)\s*\)\s*$";
        string patternJ = @"^I\d+\s*J\s*\(\s*(?<FirstIndex>\d+)\s*,\s*(?<SecondIndex>\d+)\s*,\s*(?<ThirdIndex>\d+)\s*\)\s*$";
        string DataNotion = @"^\s*DATA:\s*";

        Dictionary<string, CommandType> patternCommand;

        public Daemon()
        {
            
            patternCommand = new Dictionary<string, CommandType>();
            patternCommand.Add(patternZ, CommandType.Z);
            patternCommand.Add(patternS, CommandType.S);
            patternCommand.Add(patternT, CommandType.T);
            patternCommand.Add(patternJ, CommandType.J);

        }

        private bool ReadFile(string _directory)
        {
            bool fileOK = true;
            string[] sourceFile;
            sourceFile = File.ReadAllLines( _directory);
            int dataLine = 0;
            EventReloadInstruction(this, new EventArgs());
            for (int i = 1; i < sourceFile.Length; i++)
            {
                if (sourceFile[i].Length == 0)
                {
                    continue;
                }

                if ( System.Text.RegularExpressions.Regex.Match(sourceFile[i], DataNotion).Success )
                {
                    dataLine = i + 1;
                    break;
                }

                /* Grammar Check and Translate sourceFile to instructions, and save in instructionTable */
                if(grammarCheck(sourceFile[i]))
                {
                    EventAddInstruction(sourceFile[i]);
                   instructionTable.Add(translate(sourceFile[i]));
                }
                else
                {
                   fileOK = false;
                   break;
                }
            }

            if (fileOK)
            {
                string[] data = sourceFile[dataLine].Split(',');
                Start.Clear();
                for (int i = 0; i < data.Length; i++)
                {
                    Start.Add(Convert.ToInt32(data[i]));
                }

                setStart();
            }

            return fileOK;
        }

        private bool grammarCheck(string instruction)
        {
           return System.Text.RegularExpressions.Regex.IsMatch(instruction, "(" + patternZ + "|" + patternS + "|" + patternT + "|" + patternJ + ")");
        }
        
        private Instruction translate(string _instruction)
        {
           Instruction instruction = new Instruction();

           foreach(string pattern in patternCommand.Keys)
		   {
		       Match m = System.Text.RegularExpressions.Regex.Match(_instruction, pattern);
		       if( m.Success )
		       {
		          /* Romain: Ugly but should work
		          TODO Benny: Since you know the syntax better than I do, feel free to correct this */
		          instruction.Command = patternCommand[pattern];

                  instruction.FirstIndex = Convert.ToInt32(m.Groups["FirstIndex"].Value);
                  
                  
		          if (m.Groups["SecondIndex"].Value != "") {
		             instruction.SecondIndex = Convert.ToInt32(m.Groups["SecondIndex"].Value);
		          }
                  if (m.Groups["ThirdIndex"].Value != "") {
                     instruction.ThirdIndex = Convert.ToInt32(m.Groups["ThirdIndex"].Value);
                  }
		          break;
		       }
		   }
		   
           return instruction;
        }

        private bool instructionDo()
        {
           if (index < instructionTable.Count) 
           {
               Instruction instruction = instructionTable[index];
               EventPrepareDoInstruction(index+1, instruction, RegisterState);
               EventPrintf(computationText(index+1, RegisterState));
	           // Not sure if the switch will work, if it doesn't we'll have to use if ... then ... else
               // Switch will work well :)
               int value;
               switch(instruction.Command) 
               {
	              case CommandType.Z:
                       value = 0;
                     RegisterState.Set(instruction.FirstIndex, 0);
                     EventRegisterOperate(instruction.FirstIndex, ref value, RegisterOperateType.ToZero);
	                 index++;
	                 break;
	              case CommandType.S:
                     value = RegisterState.Get(instruction.FirstIndex) + 1;
                     RegisterState.Set(instruction.FirstIndex, value);
                     EventRegisterOperate(instruction.FirstIndex, ref value, RegisterOperateType.Increase);
	                 index++;
	                 break;
	              case CommandType.T:
                     value = 0;
                     EventRegisterOperate(instruction.FirstIndex, ref value, RegisterOperateType.Read);
                     RegisterState.Set(instruction.SecondIndex, RegisterState.Get(instruction.FirstIndex));
                     EventRegisterOperate(instruction.SecondIndex, ref value, RegisterOperateType.Write);   

	                 index++;
	                 break;
	              case CommandType.J:
                     value = 0;
                     EventRegisterOperate(instruction.FirstIndex, ref value, RegisterOperateType.Read);
                     EventRegisterOperate(instruction.SecondIndex, ref value, RegisterOperateType.Read);
                     if (RegisterState.Get(instruction.FirstIndex) == RegisterState.Get(instruction.SecondIndex))
                     {
                         index = instruction.ThirdIndex-1;
	                 } 
                     else 
                     {
	                    index++;
	                 }
	                 break;
	              default:
	                 // TODO: WTH!?
	                 break;
	           }

               return false;

           }

           return true;
           
            //event ShowInstruction once
            
            //somecommand

            //event RegisterOperate  happenned here ( maybe more than one time )

            //return true;//this is a fake return;

            //when this is the last instruction, return true;
            //else return false;
            
        }

        public void loadFile(string _directory )
        {
            this.instructionTable = new List<Instruction>();
            this.reset();
            if (!ReadFile(_directory))
            {
                Error("FILE ERROR");
            }
        }

        public void StepByStep()
        {
            if (instructionDo())
            {
                EventPrintf(resultText(RegisterState));                 //show the result
                Error("Program End");
                reset();
                return;
            }

            return;
        }

        public void DoAll()
        {
            for (int i = 0; i < 10000; i++)
            {
                if (instructionDo())
                {
                    EventPrintf(resultText(RegisterState));             //show the result
                    Error("Program End");
                    reset();
                    
                    return;
                }
            }
            EventPrintf(undefineResult());                              //show undefine result
            Error("Infinite Loop");
            reset();
            return;
        }

        public void setStart()
        {
            RegisterState = new Urm();
            index = 0;
            for( int i = 0; i<Start.Count;i++ )
            {
                int value = Start[i];
                RegisterState.Set(i+1, value);
                
            }
            EventSetRegisterStatus(this.RegisterState);
            EventSetStartFinish(this, new EventArgs());
            this.initData = true;
            return;
        }

        private string computationText(int _index, Urm _registerState)
        {
            string output = ",\r\n(";
            string urmstatus = "(";

            if (initData)
            {
                output = "COMPUTATION:\r\n(";
            }
            initData = false;
            foreach (int value in _registerState.GetAll())
            {
                urmstatus += value + ",";
            }
            urmstatus += ')';

            output = output + _index + "," + urmstatus.Replace(",)", ")") + ")";

            return output;
        }

        private string resultText(Urm _registerState)
        {
            return "\r\nRESULT:\r\n" + _registerState.Get(1) + "\r\n\r\n\r\n";
        }

        private string undefineResult()
        {
            return "\r\nRESULT:\r\nUNDEFINED\r\n\r\n\r\n";
        }

        private void reset()
        {
            this.initData = false;
            this.index = 0;
            this.RegisterState = new Urm();
            //this.Start = new List<int>();
            //this.instructionTable = new List<Instruction>();
        }

        
    }
}
